package org.datascooter.db.postgresql;

import java.text.MessageFormat;
import java.util.List;

import org.datascooter.bundle.DSMapper;
import org.datascooter.bundle.EntityBundle;
import org.datascooter.bundle.attribute.PersistAttribute;
import org.datascooter.exception.EntityNotMappedException;
import org.datascooter.impl.DataSnip;
import org.datascooter.impl.DataSnipFactory;
import org.datascooter.impl.Snip;
import org.datascooter.impl.SnipBuilder;
import org.datascooter.inface.ISnipBuilder;
import org.datascooter.meta.Column;
import org.datascooter.meta.MetaColumn;
import org.datascooter.utils.DBType;
import org.datascooter.utils.DSSettings;
import org.datascooter.utils.SnipUtils;
import org.datascooter.utils.policy.SnipType;

public class PostgresqlSnipBuilder extends SnipBuilder {

	public PostgresqlSnipBuilder() {
		super("postgresql");
	}

	@Override
	public ISnipBuilder copy() {
		return new PostgresqlSnipBuilder();
	}

	@Override
	public String managePage(DataSnip dataSnip, String sqlClause) {
		if (dataSnip.fetchSize > 0) {
			managePageParams(dataSnip, params);
			return sqlClause + " OFFSET  ? LIMIT ? ";
		}
		return sqlClause;
	}

	@Override
	public Snip buildUpdateColumnSnip(String entity, PersistAttribute attribute) throws EntityNotMappedException, SecurityException, NoSuchMethodException {
		EntityBundle bundle = DSMapper.getBundle(entity);
		DataSnip create = DataSnipFactory.create(entity);
		create.setBundle(bundle);
		return new Snip(null, SnipType.EXECUTE, MessageFormat.format("ALTER TABLE {0} ALTER COLUMN {1}", bundle.getTableName(), buildUpdateColumnClause(create, attribute)), null, null, null, -1, null);
	}

	@Override
	public void managePageParams(DataSnip dataSnip, List<Object> paramList) {
		paramList.add(Long.valueOf(dataSnip.fromRow + ""));
		paramList.add(Long.valueOf(dataSnip.fetchSize + ""));
	}

	@Override
	public String getCaseSensitiveTableName(String tableName) {
		return tableName.toLowerCase();
	}

	protected String buildUpdateColumnClause(DataSnip dataSnip, PersistAttribute attribute) {
		DBType type = attribute.getType();
		String typeStr = getDbTypeString(type);
		String size = buildDataLength(attribute.getScale(), attribute.getPrecision());
		switch (type) {
			case BIGDECIMAL:
				typeStr = "decimal";
				size = "(22,2)";
				break;
			case LONG:
				typeStr = "bigint";
				size = "";
				break;
			case BOOLEAN:
				typeStr = "boolean";
				size = "";
				break;
			case INT:
				typeStr = "integer";
				size = "";
				break;
			case STRING:
				typeStr = "varchar";
				if (size.length() == 0) {
					size = "(" + DSSettings.getDefaultStringLength() + ")";
				}
				break;
			case CHAR:
				typeStr = "char";
				if (size.length() == 0) {
					size = "(" + DSSettings.getDefaultStringLength() + ")";
				}
				break;
			case BLOB:
				typeStr = "bytea";
				if (size.length() == 0) {
					size = "";
				}
				break;
			case CLOB:
				typeStr = "text";
				if (size.length() != 0) {
					size = "";
				}
				break;
			case TIMESTAMP:
				typeStr = "timestamp";
				if (size.length() == 0) {
					size = "";
				}
				break;
		}
		return SnipUtils.SP + attribute.getColumnName() + SnipUtils.SP + "TYPE " + typeStr + SnipUtils.SP + size + (attribute.isNullable() ? SnipUtils.NN : SnipUtils.NOT_NULL);
	}

	@SuppressWarnings("nls")
	@Override
	public String buildColumnClause(PersistAttribute attribute) {
		DBType type = attribute.getType();
		String typeStr = getDbTypeString(type);
		String size = buildDataLength(attribute.getScale(), attribute.getPrecision());
		switch (type) {
			case BIGDECIMAL:
				typeStr = "decimal";
				size = "(22,2)";
				break;
			case LONG:
				typeStr = "bigint";
				size = "";
				break;
			case BOOLEAN:
				typeStr = "boolean";
				size = "";
				break;
			case INT:
				typeStr = "integer";
				size = "";
				break;
			case STRING:
				typeStr = "varchar";
				if (size.length() == 0) {
					size = "(" + DSSettings.getDefaultStringLength() + ")";
				}
				break;
			case CHAR:
				typeStr = "char";
				if (size.length() == 0) {
					size = "(" + DSSettings.getDefaultStringLength() + ")";
				}
				break;
			case BLOB:
				typeStr = "bytea";
				if (size.length() == 0) {
					size = "";
				}
				break;
			case CLOB:
				typeStr = "text";
				if (size.length() != 0) {
					size = "";
				}
				break;
			case TIMESTAMP:
				typeStr = "timestamp";
				if (size.length() == 0) {
					size = "";
				}
				break;
		}
		return SnipUtils.SP + attribute.getColumnName() + SnipUtils.SP + typeStr + SnipUtils.SP + size + (attribute.isNullable() ? SnipUtils.NN : SnipUtils.NOT_NULL);
	}

	@Override
	public boolean equalType(PersistAttribute attribute, Column column) {
		boolean result = false;
		boolean scaled = true;
		if (attribute.getType() == null) {
			return true;
		}
		switch (attribute.getType()) {
			case BIGDECIMAL:
				result = "numeric".equalsIgnoreCase(column.dataTypeName);
				break;
			case LONG:
				result = "int8".equalsIgnoreCase(column.dataTypeName);
				scaled = false;
				break;
			case BOOLEAN:
				result = "bool".equalsIgnoreCase(column.dataTypeName);
				break;
			case INT:
				result = "int4".equalsIgnoreCase(column.dataTypeName);
				scaled = false;
				break;
			case STRING:
				result = "varchar".equalsIgnoreCase(column.dataTypeName);
				break;
			case CHAR:
				result = "bpchar".equalsIgnoreCase(column.dataTypeName);
				break;
			case BLOB:
				result = "bytea".equalsIgnoreCase(column.dataTypeName);
				break;
			case CLOB:
				result = "text".equalsIgnoreCase(column.dataTypeName);
				break;
			case TIMESTAMP:
				result = "timestamp".equalsIgnoreCase(column.dataTypeName);
				scaled = false;
				break;
		}
		result = checkScale(attribute, column, result, scaled);
		return result;
	}

	@Override
	public String buildMetaColumnClause(MetaColumn column) {
		String typeStr = column.TYPE_NAME;
		String size = "";
		if ("numeric".equalsIgnoreCase(column.TYPE_NAME) || "decimal".equalsIgnoreCase(column.TYPE_NAME) || "double".equalsIgnoreCase(column.TYPE_NAME)) {
			size = "(22,2)";
		} else if ("bool".equalsIgnoreCase(column.TYPE_NAME)) {
			typeStr = "BOOLEAN";
		} else if ("varchar".equalsIgnoreCase(column.TYPE_NAME) || "char".equalsIgnoreCase(column.TYPE_NAME)) {
			if (column.COLUMN_SIZE == 0) {
				size = "(" + DSSettings.getDefaultStringLength() + ")";
			} else {
				size = buildDataLength(column.COLUMN_SIZE, column.DECIMAL_DIGITS);
			}
		} else if ("bpchar".equalsIgnoreCase(column.TYPE_NAME)) {
			if (column.COLUMN_SIZE != 0) {
				size = column.COLUMN_SIZE + "";
			}
		} else if ("text".equalsIgnoreCase(column.TYPE_NAME)) {
			if (column.COLUMN_SIZE == 0) {
				size = "(6000)";
			}
		}
		return SnipUtils.SP + column.COLUMN_NAME + SnipUtils.SP + typeStr + SnipUtils.SP + size + (column.IS_NULLABLE ? SnipUtils.NN : SnipUtils.NOT_NULL);
	}
}
